/*
    The NeonX Library

    Copyright (c) 2013 darkcat

    Code covered by the MIT License
*/

#pragma once

// nx...
#include "config/general.h"
// nx_traits
#include "type/typetraits.h"
// nx_auto
#include "type/typeof.h"
// nx_min, nx_max
#include "utility/tools.h"

//////////////////////////////////////////////////////////////////////////
NX_BEG
//////////////////////////////////////////////////////////////////////////

/*
    Get the begin iterator
*/

namespace private_
{
    template <typename T, bool Check_ = nx_is_pod(T)>
    struct Begin_;

    template <typename T>
    struct Begin_<T, true>
    {
        static void _(typename TraitsContainer<T>::ite_t& ite, const T& set)
        { ite = nx_min((T)0, set); }
    };

    template <typename T, size_t N>
    struct Begin_<T[N], true>
    {
        static void _(typename TraitsContainer<T[N]>::ite_t& ite, const T(& set)[N])
        { ite = (T*)(set); }
    };

    template <typename T>
    struct Begin_<T, false>
    {
        static void _(typename TraitsContainer<T>::ite_t& ite, const T& set)
        { ite = const_cast<T*>(&set)->begin(); }
    };
}

template <typename T>
struct Begin : private_::Begin_<T>
{};

template <typename T>
typename TraitsContainer<T>::ite_t begin(const T& set)
{
    typename TraitsContainer<T>::ite_t ite;
    Begin<T>::_(ite, set);
    return ite;
}

/*
    Get the end iterator
*/

namespace private_
{
    template <typename T, bool Check_ = nx_is_pod(T)>
    struct End_;

    template <typename T>
    struct End_<T, true>
    {
        static void _(typename TraitsContainer<T>::ite_t& ite, const T& set)
        { ite = nx_max((T)0, set); }
    };

    template <typename T, size_t N>
    struct End_<T[N], true>
    {
        static void _(typename TraitsContainer<T[N]>::ite_t& ite, const T(& set)[N])
        { ite = (T*)(set + N); }
    };

    template <typename T>
    struct End_<T, false>
    {
        static void _(typename TraitsContainer<T>::ite_t& ite, const T& set)
        { ite = const_cast<T*>(&set)->end(); }
    };
}

template <typename T>
struct End : private_::End_<T>
{};

template <typename T>
typename TraitsContainer<T>::ite_t end(const T& set)
{
    typename TraitsContainer<T>::ite_t ite;
    End<T>::_(ite, set);
    return ite;
}

/*
    Check iterator is inside or not
*/

namespace private_
{
    template <typename T, bool Check_ = nx_is_pod(T)>
    struct Inside_;

    template <typename T>
    struct Inside_<T, true>
    {
        template <typename T_, bool C_ = nx_is_unsigned(T_)>
        struct UnsignedSwitch_
        {
            template <typename U_>
            static bool _(U_& ite, const T_& set)
            {
                if (set > 0)
                    return ((ite >= 0) && (ite < set));
                else
                if (set < 0)
                    return ((ite < 0) && (ite >= set));
                else
                    return false;
            }
        };

        template <typename T_>
        struct UnsignedSwitch_<T_, true>
        {
            template <typename U_>
            static bool _(U_& ite, const T_& set)
            {
                if (set > 0)
                    return (ite < set);
                else
                    return false;
            }
        };

        static bool _(typename TraitsContainer<T>::ite_t& ite, const T& set)
        {
            return UnsignedSwitch_<T>::_(ite, set);
        }
    };

    template <typename T, size_t N>
    struct Inside_<T[N], true>
    {
        static bool _(typename TraitsContainer<T[N]>::ite_t& ite, const T(& set)[N])
        {
            return ((ite >= (T*)(set)) && (ite < (T*)(set + N)));
        }
    };

    template <typename T>
    struct Inside_<T, false>
    {
        static bool _(typename TraitsContainer<T>::ite_t& ite, const T& set)
        {
            typename TraitsContainer<T>::ite_t iter_end = const_cast<T*>(&set)->end();
            return (ite != iter_end);
        }
    };
}

template <typename T>
struct Inside : private_::Inside_<T>
{};

template <typename T>
bool inside(typename TraitsContainer<T>::ite_t& ite, const T& set)
{
    return Inside<T>::_(ite, set);
}

/*
    nx_foreach macro
*/

namespace private_
{
    template <typename T>
    struct foreach_set_
    {
        T set_; bool brk_;
        foreach_set_(T s) : set_(s), brk_(true) {}
    };
}

#define nx_foreach(ite, set, ...) \
    for(nx::private_::foreach_set_<nx_traits(nx_typeof(set))::param_t> \
        set_(set); set_.brk_; set_.brk_ = false) \
        for(nx_auto(ite, nx::begin(set_.set_)); nx::inside(ite, set_.set_); ++ite,##__VA_ARGS__)

#define nx_forever(...) \
    for(__VA_ARGS__;;)

//////////////////////////////////////////////////////////////////////////
NX_END
//////////////////////////////////////////////////////////////////////////
